package com.motu.vertx.module.utility.commondata;

import com.motu.vertx.module.utility.logsdk.AdjustCode;
import com.motu.vertx.module.utility.logsdk.ChargePackage;
import com.motu.vertx.module.utility.mysql.MysqlClient;
import com.motu.vertx.module.utility.mysql.MysqlTool;
import com.motu.vertx.module.utility.toolset.Tool;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;

public class CommonDataManager {

    public final static Logger logger = LoggerFactory.getLogger(CommonDataManager.class);

    //限制注册登陆配置表(key为类型)
    private static ConcurrentHashMap<Integer, List<Loginlimit>> loginlimitList = new ConcurrentHashMap<>();
    // gm托管角色信息
    private static ConcurrentHashMap<Long, GmHostedUser> hostedUserList = new ConcurrentHashMap<Long, GmHostedUser>();
    // gm账号信息
    private static Vector<GmAccount> gmAccountList = new Vector<GmAccount>();
    // 强更地址信息
    private static Vector<UpdateUrl> updateUrlList = new Vector<UpdateUrl>();
    // 亚太地区ip信息
    private static Apnic[] apnicList;
	// 第三方日志上报配置
	private static ArrayList<ChargePackage> chargePackageList = new ArrayList<ChargePackage>();
	// adjustCode配置表，key为类型+参数
	private static ConcurrentHashMap<String, AdjustCode> adjustCodeMap = new ConcurrentHashMap<String, AdjustCode>();

    // 公共数据初始化
    public static void init(List<Future> futures) {
		futures.add(initLoginlimit());
		futures.add(initHostedUser());
		futures.add(initGmAccount());
		futures.add(initUpdateurl());
		futures.add(initApnic());
		futures.add(initChargePackage());
		futures.add(initAdjustCode());
    }

    /**
     * 初始化限制注册登陆配置表
     */
    public static Future initLoginlimit() {
		Promise promise = Promise.promise();
        String table = "loginlimit";
        String sql = "select * from " + table + ";";
        MysqlTool.queryJsonArrayFromDb(MysqlClient.getServerPool(), sql, new JsonArray(), res -> {
            if (res.succeeded()) {
                ConcurrentHashMap<Integer, List<Loginlimit>> tmp = new ConcurrentHashMap<>();
                JsonArray result = res.result();
                logger.info("init " + table + " size:" + result.size());
                for (int i = 0; i < result.size(); i++) {
                    Loginlimit entity = new Loginlimit(result.getJsonObject(i));
                    List<Loginlimit> list = tmp.get(entity.getType());
                    if (list == null) {
                        list = new ArrayList<>();
                        tmp.put(entity.getType(), list);
                    }
                    list.add(entity);
                }
                loginlimitList = tmp;
				promise.complete();
            } else {
				logger.error("initLoginlimit error: " + res.cause().getMessage());
				promise.fail(res.cause());
            }
        });
		return promise.future();
    }

    /**
     * 判断注册是否有限制
     */

    public static boolean isRegisterLimit(int platform, long channel, String version){
        return checkLoginLimit(Loginlimit.REGISTER, platform, channel, version, null, null);
    }

    /**
     * 判断登陆是否有限制
     */
    public static boolean isLoginLimit(int platform, long channel, String version){
        return checkLoginLimit(Loginlimit.LOGIN, platform, channel, version, null, null);
    }

    /**
     * 判断注册是否有限制
     */
    public static boolean isRegisterLimit(int platform, long channel, String version, String deviceId){
        return checkLoginLimit(Loginlimit.REGISTER, platform, channel, version, deviceId, null);
    }

    /**
     * 判断登陆是否有限制
     */
    public static boolean isLoginLimit(int platform, long channel, String version, String deviceId){
        return checkLoginLimit(Loginlimit.LOGIN, platform, channel, version, deviceId, null);
    }

    /**
     * 判断注册是否有限制
     */
    public static boolean isRegisterLimit(int platform, long channel, String version, String deviceId, String deviceType){
        return checkLoginLimit(Loginlimit.REGISTER, platform, channel, version, deviceId, deviceType);
    }

    /**
     * 判断登陆是否有限制
     */
    public static boolean isLoginLimit(int platform, long channel, String version, String deviceId, String deviceType){
        return checkLoginLimit(Loginlimit.LOGIN, platform, channel, version, deviceId, deviceType);
    }

    private static boolean checkLoginLimit(int type, int platform, long channel, String version,
                                           String deviceId, String deviceType){
        List<Loginlimit> list = loginlimitList.get(type);
        if (list == null || list.size() == 0) {
            return false;
        }
        boolean limit = false;
        for (Loginlimit loginlimit : list) {
            // 如果客户端设备ID在白名单中，那么这条限制就不用判断了
            if (loginlimit.isDeviceIdWhiteList(deviceId)) {
                continue;
            }
            if (loginlimit.getPlatform() > 0 && !Tool.isEmpty(loginlimit.getVersion())) {
                limit = checkPCLimit(loginlimit, platform, channel) && checkVersionLimit(loginlimit, version);
            } else if (loginlimit.getPlatform() > 0) {
                limit = checkPCLimit(loginlimit, platform, channel);
            } else if (!Tool.isEmpty(loginlimit.getVersion())) {
                limit = checkVersionLimit(loginlimit, version);
            } else if (!Tool.isEmpty(loginlimit.getDeviceType()) && !Tool.isEmpty(deviceType)){
                limit = loginlimit.getDeviceType().equals(deviceType);
            }
            if (limit) {
                return true;
            }
        }
        return false;
    }

    private static boolean checkPCLimit(Loginlimit loginlimit, int platform, long channel) {
        if (loginlimit.getPlatform() > 0 && loginlimit.getChannel() > 0) {
            return platform == loginlimit.getPlatform() && channel == loginlimit.getChannel();
        } else if (loginlimit.getPlatform() > 0) {
            return platform == loginlimit.getPlatform();
        } else {
            return false;
        }
    }

    private static boolean checkVersionLimit(Loginlimit loginlimit, String version) {
        if (!Tool.isEmpty(version) && !Tool.isEmpty(loginlimit.getVersion())) {
            return !Tool.compareVersion(version, loginlimit.getVersion());
        } else {
            return false;
        }
    }

    /***
     * 初始化托管用户信息
     */
    public static Future initHostedUser() {
		Promise promise = Promise.promise();
        String sql = "SELECT * FROM gm_hosted_user;";
        MysqlTool.queryJsonArrayFromDb(MysqlClient.getServerPool(), sql, new JsonArray(), handler -> {
            if (handler.succeeded()) {
                ConcurrentHashMap<Long, GmHostedUser> tmp = new ConcurrentHashMap<Long, GmHostedUser>();
                JsonArray list = (JsonArray) handler.result();
                logger.info("******** initHostedUser ******** len:" + list.size());
                for (int i = 0; i < list.size(); i++) {
                    GmHostedUser entity = new GmHostedUser(list.getJsonObject(i));
                    tmp.put(entity.getUserId(), entity);
                }
                hostedUserList.clear();
                hostedUserList = tmp;
				promise.complete();
            } else {
                logger.error("initHostedUser " + handler.cause().getMessage());
				promise.fail(handler.cause());
            }
        });
		return promise.future();
    }

    /***
     * 判断这个用户id是否被gm托管了
     * @param accountType
     * @param accountId
     * @param userId
     * @return
     */
    public static boolean isHostedUser(int accountType, String accountId, long userId) {
        if (hostedUserList.containsKey(userId)) {
            GmHostedUser entity = hostedUserList.get(userId);
            return entity.getAccountId().equalsIgnoreCase(accountId) && entity.getAccountType() == accountType;
        }
        return false;
    }

    /***
     * 获取某个帐号下托管的所有userid
     * @param accountType
     * @param accountId
     * @return
     */
    public static JsonArray getHostedUserList(int accountType, String accountId) {
        JsonArray list = new JsonArray();
        for (GmHostedUser entity : hostedUserList.values()) {
            if (entity.getAccountId().equalsIgnoreCase(accountId) && entity.getAccountType() == accountType) {
                list.add(entity.getUserId());
            }
        }
        return list;
    }

    /***
     * 初始化gm帐号
     */
    public static Future initGmAccount() {
		Promise promise = Promise.promise();
        String sql = "select * from gm_account;";
        MysqlTool.queryJsonArrayFromDb(MysqlClient.getServerPool(), sql, new JsonArray(), handler -> {
            if (handler.succeeded()) {
                Vector<GmAccount> tmp = new Vector<GmAccount>();
                JsonArray list = (JsonArray) handler.result();
                logger.info("******** initGmAccount ******** len:" + list.size());
                for (int i = 0; i < list.size(); i++) {
                    tmp.add(new GmAccount(list.getJsonObject(i)));
                }
                gmAccountList.clear();
                gmAccountList = tmp;
				promise.complete();
            } else {
                logger.error("initGmAccount " + handler.cause().getMessage());
				promise.fail(handler.cause());
            }
        });
		return promise.future();
    }

    /***
     * 判断是否gm帐号
     * @param accountType
     * @param accountId 帐号id
     * @return
     */
    public static boolean isGmAccount(int accountType, String accountId) {
        for (int i = 0; i < gmAccountList.size(); i++) {
            GmAccount entity = gmAccountList.elementAt(i);
            if (entity.getAccountId().equalsIgnoreCase(accountId) && entity.getAccountType() == accountType) {
                return true;
            }
        }
        return false;
    }

    /***
     * 初始化客户端包更新配置表
     */
    public static Future initUpdateurl() {
		Promise promise = Promise.promise();
        String sql = "select * from update_url;";
        MysqlTool.queryJsonArrayFromDb(MysqlClient.getServerPool(), sql, new JsonArray(), handler -> {
            if (handler.succeeded()) {
                Vector<UpdateUrl> tmp = new Vector<UpdateUrl>();
                JsonArray list = (JsonArray) handler.result();
                logger.info("******** initUpdateurl ******** len:" + list.size());
                for (int i = 0; i < list.size(); i++) {
                    tmp.add(new UpdateUrl(list.getJsonObject(i)));
                }
                updateUrlList.clear();
                updateUrlList = tmp;
				promise.complete();
            } else {
                logger.error("initUpdateurl " + handler.cause().getMessage());
				promise.fail(handler.cause());
            }
        });
		return promise.future();
    }

    /***
     * 根据platform和channel获取更新包信息
     * @param platform
     * @param channel
     * @return
     */
    public static UpdateUrl getUpdateurl(String packageName, int platform, long channel){
        for(UpdateUrl entity : updateUrlList){
            if(entity.getPackageName().equals(packageName)
                    && entity.getPlatform()==platform && entity.getChannel()==channel){
                return entity;
            }
        }
        return null;
    }

    /**
     * 初始化亚太地区ip信息
     */
    public static Future initApnic() {
		Promise promise = Promise.promise();
        String sql = "SELECT cc,start,value FROM apnic where type = 'ipv4' order by id;";
        MysqlTool.queryJsonArrayFromDb(MysqlClient.getCommonPool(), sql, new JsonArray(), res -> {
            if (res.succeeded()) {
                JsonArray list = res.result();
                Apnic[] tmp = new Apnic[list.size()];
                logger.info("initApnic size:" + list.size());
                for (int i = 0; i < list.size(); i++) {
                    JsonObject obj = list.getJsonObject(i);
                    Apnic apnic = new Apnic(obj);
                    tmp[i] = apnic;
                }
                apnicList = tmp;
				promise.complete();
            } else {
                logger.error("initApnic error " + res.cause().getMessage());
				promise.fail(res.cause());
            }
        });
		return promise.future();
    }

    /***
     * 根据ip取出地区代码（二分法查找）
     * @param ip
     * @return
     */
    public static String getIpRegionCode(String ip) {
        if(ip != null && !ip.isEmpty()) {
            try{
                if (Tool.isIpv6(ip)){
                    return "Unknown";//暂时不支持ipv6
                }
            }catch (Exception e){
                return "Unknown";//不是合法的ip地址
            }
            long ipLong = Tool.ip2long(ip);
            int low=0;
            if(apnicList != null) {
                int high = apnicList.length - 1;
                while (low <= high) {
                    int mid = low + (high - low) / 2;
                    long min = apnicList[mid].getLongValue();
                    long max = min + apnicList[mid].getValue() - 1;
                    if (ipLong >= min && ipLong <= max) {
                        return apnicList[mid].getCc();
                    }
                    if (ipLong > max) {
                        low = mid + 1;
                    }
                    if (ipLong < min) {
                        high = mid - 1;
                    }
                }
            }
        }
        return "Unknown";//没有找到返回-1
    }

	/**
	 * 初始化第三方日志上报配置
	 */
	public static Future initChargePackage() {
		Promise promise = Promise.promise();
		String sql = "SELECT * FROM charge_package;";
		MysqlTool.queryJsonArrayFromDb(MysqlClient.getServerPool(), sql, new JsonArray(), handler -> {
			if (handler.succeeded()) {
				ArrayList<ChargePackage> tmp = new ArrayList<ChargePackage>();
				JsonArray array = handler.result();
				logger.info("#### initChargePackage size:" + array.size());
				for (int i = 0, size = array.size(); i < size; i++) {
					JsonObject obj = array.getJsonObject(i);
					ChargePackage rd = new ChargePackage(obj);
					tmp.add(rd);
				}
				chargePackageList.clear();
				chargePackageList = tmp;
				promise.complete();
			} else {
				logger.error("#### initChargePackage error:" + handler.cause().getMessage());
				promise.fail(handler.cause());
			}
		});
		return promise.future();
	}

	/**
	 * 初始化AdjustCode配置
	 */
	public static Future initAdjustCode() {
		Promise promise = Promise.promise();
		String sql = "SELECT * FROM adjust_code;";
		MysqlTool.queryJsonArrayFromDb(MysqlClient.getServerPool(), sql, new JsonArray(), handler -> {
			if (handler.succeeded()) {
				JsonArray array = handler.result();
				logger.info("#### initAdjustCode size:" + array.size());
				ConcurrentHashMap<String, AdjustCode> temp = new ConcurrentHashMap<String, AdjustCode>();
				for (int i = 0, size = array.size(); i < size; i++) {
					JsonObject obj = array.getJsonObject(i);
					AdjustCode model = new AdjustCode(obj);
					temp.put(getKey(model.getType(), model.getParam()), model);
				}
				adjustCodeMap = temp;
				promise.complete();
			} else {
				logger.error("#### initAdjustCode error:" + handler.cause().getMessage());
				promise.fail(handler.cause());
			}
		});
		return promise.future();
	}

	public static ChargePackage getChargePackage(int type, int platform, long channel) {
		ArrayList<ChargePackage> chargePackageList = getChargePackageList();
		for (ChargePackage cp : chargePackageList) {
			if (cp.getPlatform() == platform && cp.getChannel() == channel && cp.getType() == type) {
				return cp;
			}
		}
		return null;
	}

	public static ArrayList<ChargePackage> getChargePackageList() {
		return chargePackageList;
	}

	/***
	 * 获取组合key
	 */
	public static String getKey(Object... keys) {
		String key = "";
		for (int i = 0; i < keys.length; i++) {
			if (i == keys.length - 1) { // 是最后一个
				key += keys[i];
			} else {
				key += keys[i] + "_";
			}
		}
		return key;
	}

	public static AdjustCode getAdjustCode(int type, int param) {
		return adjustCodeMap.get(getKey(type, param));
	}

	public static ConcurrentHashMap<String, AdjustCode> getAdjustCodeMap() {
		return adjustCodeMap;
	}

}
